# backend/__init__.py
import os
import logging
from datetime import datetime
from flask import Flask, render_template, send_from_directory, request, redirect, url_for, flash, jsonify, abort
from flask_migrate import Migrate
from flasgger import Swagger
from flask_jwt_extended import JWTManager
from backend.infrastructure.database import db
from backend.decorators.auth_decorators import require_permissions

# 确保环境变量被正确加载
try:
    from dotenv import load_dotenv
    load_dotenv()
except ImportError:
    pass  # dotenv 不是必需的

def create_app():
    app = Flask(
        __name__,
        template_folder=os.path.join(os.getcwd(), 'frontend', 'templates'),
        static_folder=os.path.join(os.getcwd(), 'frontend', 'static')
    )
    from config import config as app_config
    env = os.getenv('FLASK_ENV', 'default')
    app.config.from_object(app_config[env])
    
    swagger = Swagger(app)
    app.config['JWT_TOKEN_LOCATION'] = ['headers', 'cookies']
    app.config['JWT_ACCESS_COOKIE_NAME'] = 'access_token'
    app.config['JWT_COOKIE_CSRF_PROTECT'] = False
    # 设置token过期时间为5分钟，通过活动续期来延长使用时间
    from datetime import timedelta
    app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=5)
    jwt = JWTManager(app)
    # 业务配置项
    try:
        from backend.constants import (
            CHART_COLORS, SCHOOL_OPTIONS, INTERVENTION_OPTIONS,
            ACUPOINT_OPTIONS, PRESS_METHOD_OPTIONS
        )
        app.config['CHART_COLORS'] = CHART_COLORS
    except Exception:
        pass
    
    # 生产环境使用INFO级别，开发环境可使用DEBUG
    log_level = logging.INFO if app.config.get('ENV') == 'production' else logging.DEBUG
    logging.basicConfig(level=log_level)
    app.logger.setLevel(logging.INFO)
    
    # 输出（脱敏）数据库连接串（放到 logging.basicConfig 之后，避免被默认 WARNING 级别过滤）
    try:
        raw_uri = app.config.get('SQLALCHEMY_DATABASE_URI', '')
        masked_uri = raw_uri
        if '://' in raw_uri:
            scheme, rest = raw_uri.split('://', 1)
            if '@' in rest and ':' in rest.split('@')[0]:
                cred, tail = rest.split('@', 1)
                user = cred.split(':', 1)[0]
                masked_uri = f"{scheme}://{user}:***@{tail}"
        logging.info('[DB] 当前使用的数据库URI(已脱敏): %s', masked_uri)
    except Exception:
        pass
    # 针对 sqlite (测试/本地快速场景) 自动剔除 MySQL 专用 connect_timeout，避免无效参数报错
    try:
        uri = app.config.get('SQLALCHEMY_DATABASE_URI', '')
        if uri.startswith('sqlite:'):
            engine_opts = app.config.get('SQLALCHEMY_ENGINE_OPTIONS') or {}
            ca = engine_opts.get('connect_args') or {}
            if 'connect_timeout' in ca:  # 仅测试/开发 sqlite 需要移除
                ca.pop('connect_timeout', None)
                engine_opts['connect_args'] = ca
                app.config['SQLALCHEMY_ENGINE_OPTIONS'] = engine_opts
                app.logger.debug('[SQLITE_SANITIZE] removed connect_timeout from connect_args for sqlite URI')
    except Exception as _s_e:  # noqa: BLE001
        app.logger.debug('[SQLITE_SANITIZE] skip sanitize error=%s', _s_e)

    db.init_app(app)
    migrate = Migrate(app, db)
    # 初始化数据库错误计数
    app.config['DB_ERROR_COUNT'] = 0

    # 启动期数据库探针（带重试，避免 MySQL 较慢就绪导致一次性失败制造困惑）
    # 测试环境(TestingConfig) 与 sqlite 场景跳过该探针，避免不支持的 connect_args 或多余重试噪音
    if not app.config.get('TESTING') and not app.config.get('SQLALCHEMY_DATABASE_URI','').startswith('sqlite'):  # sqlite/测试环境直接跳过探针
        try:
            from sqlalchemy import text
            import time as _time
            with app.app_context():
                attempts = 5
                for i in range(1, attempts + 1):
                    t0 = _time.time()
                    try:
                        probe_res = db.session.execute(text('SELECT 1')).scalar()
                        elapsed = (_time.time() - t0) * 1000
                        # 仅在首次成功时记录
                        if i == 1:
                            app.logger.info('[DB_PROBE] connected successfully in %.2fms', elapsed)
                        break
                    except Exception as _pe:  # noqa: BLE001
                        if i == attempts:
                            app.logger.error('[DB_PROBE] final fail after %d attempts: %s', attempts, _pe)
                        else:
                            _time.sleep(1)
        except Exception as _e:  # 总体保护
            app.logger.error('[DB_PROBE] unexpected wrapper error=%s', _e)

    with app.app_context():
        try:
            from backend.models.student import Student
            from backend.models.student_extension import StudentExtension
            from backend.models.vision import VisionRecord
            from backend.models.vision_log import VisionRecordLog, LogAction
            from backend.models.intervention import InterventionRecord
            from backend.models.intervention_log import InterventionRecordLog
            from backend.models.followup import FollowupRecord
            from backend.models.followup_log import FollowupRecordLog
            from backend.models.user import User
            from backend.models.role_application import RoleApplication  # 确保RoleApplication模型被导入
        except Exception as e:
            app.logger.error(f"Database initialization failed: {str(e)}")
            raise
    try:
        from backend.api.query_api import query_students
        from backend.api.import_api import import_api
        from backend.api.sidebar_api import sidebar_api
        from backend.api.query_api import query_api
        from backend.api.analysis_api import analysis_api
        from backend.api.user_api import user_api
        from backend.api.user_admin_enhanced_api import user_admin_enhanced_api
        from backend.api.chart_api import chart_api
        from backend.api.report_api import report_api
        from backend.api.intervention_api import intervention_api
        from backend.api.followup_api import followup_api
        from backend.api.vision_api import vision_api
        from backend.api.student_complete_api import student_complete_api
        from backend.api.config_api import config_api
        
        # Dashboard API - 统一仪表板数据接口
        from backend.api.dashboard_api import dashboard_api
        
        # 新增权限管理API（不包含登录注册，避免冲突）
        from backend.api.role_application_api import role_application_api
        
        app.register_blueprint(chart_api)
        app.register_blueprint(report_api)
        app.register_blueprint(analysis_api)
        app.register_blueprint(import_api)
        app.register_blueprint(sidebar_api)
        app.register_blueprint(query_api)
        app.register_blueprint(user_api, url_prefix='/api')
        app.register_blueprint(user_admin_enhanced_api, url_prefix='/api')
        app.register_blueprint(intervention_api)
        app.register_blueprint(followup_api)
        app.register_blueprint(vision_api)
        app.register_blueprint(student_complete_api)
        app.register_blueprint(config_api)
        app.register_blueprint(dashboard_api)  # 注册仪表板API
        
        # 注册缓存管理API
        from backend.api.cache_api import cache_api
        app.register_blueprint(cache_api)
        
        # 注册权限管理API
        app.register_blueprint(role_application_api)
        
        from backend.api.auth import auth_bp
        app.register_blueprint(auth_bp)
        
        # SQLAlchemy 事件钩子统计连接错误
        try:
            from sqlalchemy import event
            from sqlalchemy.exc import OperationalError
            # 使用 db.engine 代替 deprecated db.get_engine(app)
            engine = db.engine
            @event.listens_for(engine, 'handle_error')
            def count_db_errors(context):  # pylint: disable=unused-variable
                if isinstance(context.original_exception, OperationalError):
                    app.config['DB_ERROR_COUNT'] = app.config.get('DB_ERROR_COUNT', 0) + 1
        except Exception:
            pass
        
        # 定义admin蓝图并注册路由
        from flask import Blueprint
        admin_bp = Blueprint('admin', __name__, url_prefix='/api/admin')
        
        # 导入admin模块的路由函数
        from backend.api.admin.role_admin_api import (
            list_roles, add_role, edit_role, remove_role,
            get_role_permissions, update_role_permissions, get_all_permissions,
            get_all_roles, get_all_permissions_simple
        )
        from backend.api.admin.permission_admin_api import list_permissions, add_permission, edit_permission, remove_permission
        from backend.api.admin.user_role_api import list_user_roles, update_user_roles
        from backend.api.admin.role_permission_api import list_role_permissions, update_role_permissions
        from backend.api.admin.system_log_api import list_system_logs, get_system_log_detail, get_system_log_stats, restore_system_log

        from backend.api.user_api import admin_required
        
        # 导入新增的管理模块蓝图
        from backend.api.admin.document_management_api import document_management_api
        from backend.api.admin.notification_management_api import notification_management_api
        from backend.api.admin.feedback_management_api import feedback_management_api
        from backend.api.admin.doctor_consultation_api import doctor_consultation_api
        
        # 注册admin路由
        
        admin_bp.add_url_rule('/roles', 'list_roles', list_roles, methods=['GET'])
        admin_bp.add_url_rule('/roles', 'add_role', add_role, methods=['POST'])
        admin_bp.add_url_rule('/roles/<int:role_id>', 'edit_role', edit_role, methods=['PUT'])
        admin_bp.add_url_rule('/roles/<int:role_id>', 'remove_role', remove_role, methods=['DELETE'])
        
        # 新增：角色权限绑定管理路由
        admin_bp.add_url_rule('/roles/all', 'get_all_roles', get_all_roles, methods=['GET'])
        admin_bp.add_url_rule('/roles/<int:role_id>/permissions', 'get_role_permissions', get_role_permissions, methods=['GET'])
        admin_bp.add_url_rule('/roles/<int:role_id>/permissions', 'update_role_permissions', update_role_permissions, methods=['PUT'])
        admin_bp.add_url_rule('/permissions/all', 'get_all_permissions', get_all_permissions, methods=['GET'])
        admin_bp.add_url_rule('/permissions/simple', 'get_all_permissions_simple', get_all_permissions_simple, methods=['GET'])
        
        admin_bp.add_url_rule('/permissions', 'list_permissions', list_permissions, methods=['GET'])
        admin_bp.add_url_rule('/permissions', 'add_permission', add_permission, methods=['POST'])
        admin_bp.add_url_rule('/permissions/<path:permission_id>', 'edit_permission', edit_permission, methods=['PUT'])
        admin_bp.add_url_rule('/permissions/<int:permission_id>', 'remove_permission', remove_permission, methods=['DELETE'])
        
        admin_bp.add_url_rule('/users/<int:user_id>/roles', 'list_user_roles', list_user_roles, methods=['GET'])
        admin_bp.add_url_rule('/users/<int:user_id>/roles', 'update_user_roles', update_user_roles, methods=['POST'])
        
        # 系统日志管理路由
        admin_bp.add_url_rule('/logs', 'list_system_logs', list_system_logs, methods=['GET'])
        admin_bp.add_url_rule('/logs/<int:log_id>', 'get_system_log_detail', get_system_log_detail, methods=['GET'])
        admin_bp.add_url_rule('/logs/stats', 'get_system_log_stats', get_system_log_stats, methods=['GET'])
        admin_bp.add_url_rule('/logs/<int:log_id>/restore', 'restore_system_log', restore_system_log, methods=['POST'])
        
        app.register_blueprint(admin_bp)
        
        # 注册新增的管理模块蓝图
        app.register_blueprint(document_management_api)
        app.register_blueprint(notification_management_api)
        app.register_blueprint(feedback_management_api)  
        app.register_blueprint(doctor_consultation_api)
        
        # 注册小程序API模块
        try:
            from backend.api.miniprogram import init_miniprogram_api
            init_miniprogram_api(app)
        except Exception as e:
            app.logger.error(f"Mini-program API registration error: {str(e)}")
            # 小程序API注册失败不影响主系统运行
            pass
    except Exception as e:
        app.logger.error(f"Blueprint registration error: {str(e)}")
        raise

    # 页面主路由
    @app.route('/')
    def welcome():
        return render_template('welcome.html')

    # (已移除) 旧 /uploads/images/<filename> 路由，统一改用 /media 体系

    @app.route('/login', methods=['GET'])
    def login():
        return render_template('welcome.html')

    @app.route('/import')
    def data_import():
        return render_template('data_import.html')

    @app.route('/index')
    def index():
        from flask_jwt_extended import verify_jwt_in_request, get_jwt_identity
        try:
            verify_jwt_in_request()
            user = get_jwt_identity()
        except Exception:
            return redirect(url_for('welcome'))
        
        # 获取统计数据传递给模板
        try:
            from backend.models.student import Student
            from backend.models.vision import VisionRecord
            from backend.models.intervention import InterventionRecord
            from backend.models.followup import FollowupRecord
            
            total_students = db.session.query(Student).count()
            total_vision = db.session.query(VisionRecord).count()
            total_intervention = db.session.query(InterventionRecord).count()
            total_followup = db.session.query(FollowupRecord).count()
            
        except Exception as e:
            app.logger.warning(f"获取统计数据失败: {str(e)}")
            # 使用默认值确保页面正常显示
            total_students = 2008
            total_vision = 1856
            total_intervention = 892
            total_followup = 456
        
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        return render_template('index.html', 
                             current_time=current_time,
                             total_students=total_students,
                             total_vision=total_vision,
                             total_intervention=total_intervention,
                             total_followup=total_followup)

    @app.route('/query')
    def query_page():
        from flask_jwt_extended import verify_jwt_in_request
        try:
            verify_jwt_in_request()
        except Exception:
            return redirect(url_for('welcome'))
        return render_template('query.html')

    # 统一媒体访问路由 (Stage1)
    @app.route('/media/<media_type>/<path:subpath>')
    def serve_media(media_type, subpath):  # pragma: no cover - IO
        """统一媒体访问: /media/<type>/<category>/YYYY/MM/DD/<filename>
        仅处理存在于 MEDIA_ROOT 下的路径，禁止越级。
        """
        from flask import abort, send_from_directory
        from werkzeug.utils import safe_join
        media_root = app.config.get('MEDIA_ROOT')
        if not media_root:
            return abort(404)
        # 只允许已知类型
        if media_type not in {'image', 'video', 'audio', 'file'}:
            return abort(404)
        # 组合物理路径
        try:
            physical_dir = safe_join(media_root, media_type)
            # subpath 可能包含多级目录，send_from_directory 需目录 + 文件名
            # 拆分 subpath 最后部分为文件名
            import os
            parts = subpath.split('/')
            filename = parts[-1]
            rel_dir = '/'.join(parts[:-1])
            target_dir = safe_join(physical_dir, rel_dir)
            if not os.path.exists(os.path.join(target_dir, filename)):
                return abort(404)
            return send_from_directory(target_dir, filename)
        except Exception:
            return abort(404)

    # 已移除“小程序媒体访问兼容”接口: /api/miniprogram/media/...  —— 新数据只允许标准 /media/... 访问。
    # 若仍有前端请求旧兼容路径，应直接 404 以暴露问题，方便尽早修复。

    @app.route('/report')
    def report_page():
        from flask_jwt_extended import verify_jwt_in_request
        try:
            verify_jwt_in_request()
        except Exception:
            return redirect(url_for('welcome'))
        return render_template('report.html')

    @app.route('/chart')
    def chart_page():
        from flask_jwt_extended import verify_jwt_in_request
        try:
            verify_jwt_in_request()
        except Exception:
            return redirect(url_for('welcome'))
        return render_template('chart.html')

    @app.route('/user-management')
    @require_permissions('user:read')
    def user_management():
        return render_template('user_management.html')

    @app.route('/role-management')
    @require_permissions('role:manage')
    def role_management():
        return render_template('role_management.html')

    @app.route('/token-test')
    def token_test():
        return render_template('token_test.html')
    
    @app.route('/debug-token')
    def debug_token():
        from flask_jwt_extended import verify_jwt_in_request, get_jwt
        try:
            verify_jwt_in_request()
            claims = get_jwt()
            return {
                'success': True,
                'claims': claims,
                'user_roles': claims.get('roles', []),
                'has_admin': 'admin' in claims.get('roles', [])
            }
        except Exception as e:
            return {
                'success': False,
                'error': str(e)
            }

    @app.route('/permission-management')
    @require_permissions('permission:manage')
    def permission_management():
        return render_template('permission_management.html')

    @app.route('/operation-log')
    @require_permissions('log:read')
    def operation_log():
        return render_template('operation_log.html')

    @app.route('/document-management')
    @require_permissions('system:manage')
    def document_management():
        return render_template('document_management.html')

    @app.route('/notification-management')
    @require_permissions('system:manage')
    def notification_management():
        return render_template('notification_management.html')

    @app.route('/feedback-management')
    @require_permissions('feedback:read')
    def feedback_management():
        return render_template('feedback_management.html')

    @app.route('/doctor-consultation')
    @require_permissions('social:read')
    def doctor_consultation():
        return render_template('doctor_consultation.html')

    @app.route('/test-admin-apis')
    def test_admin_apis():
        """API测试页面 - 用于验证修复后的token认证"""
        return render_template('test_admin_apis.html')

    @app.route('/personal-center')
    def personal_center():
        from flask_jwt_extended import verify_jwt_in_request
        try:
            verify_jwt_in_request()
        except Exception:
            return redirect(url_for('welcome'))
        return render_template('personal_center.html')

    @app.route('/api/students/import', methods=['POST'])
    def import_students():
        app.logger.debug("Received upload request")
        return jsonify({"message": "上传成功"}), 200

    @app.route('/interventions')
    @app.route('/intervention/record')
    def intervention_record_page():
        from flask_jwt_extended import verify_jwt_in_request
        try:
            verify_jwt_in_request()
        except Exception:
            return redirect(url_for('welcome'))
        student_id = request.args.get('student_id')
        auto_expand = request.args.get('auto_expand')
        from_complete = request.args.get('from_complete')
        try:
            from backend.constants import SCHOOL_OPTIONS, INTERVENTION_OPTIONS, ACUPOINT_OPTIONS, PRESS_METHOD_OPTIONS
        except Exception:
            SCHOOL_OPTIONS = INTERVENTION_OPTIONS = ACUPOINT_OPTIONS = PRESS_METHOD_OPTIONS = None
        return render_template(
            'intervention_record.html',
            SCHOOL_OPTIONS=SCHOOL_OPTIONS,
            INTERVENTION_OPTIONS=INTERVENTION_OPTIONS,
            ACUPOINT_OPTIONS=ACUPOINT_OPTIONS,
            PRESS_METHOD_OPTIONS=PRESS_METHOD_OPTIONS,
            current_student_id=student_id if student_id else None,
            auto_expand=auto_expand,
            from_complete=from_complete
        )

    @app.route('/interventions_page')
    def interventions_page():
        return redirect(url_for('handle_intervention'))

    @app.route('/favicon.ico')
    def favicon():
        try:
            # 使用app.static_folder确保路径正确
            return send_from_directory(app.static_folder,
                                    'favicon.ico', mimetype='image/vnd.microsoft.icon')
        except FileNotFoundError:
            abort(404)

    @app.route('/phone-followup')
    def phone_followup_page():
        from flask_jwt_extended import verify_jwt_in_request
        try:
            verify_jwt_in_request()
        except Exception:
            return redirect(url_for('welcome'))
        return render_template('phone_followup.html')

    @app.route('/student_detail/<int:student_id>')
    def student_detail(student_id):
        from flask_jwt_extended import verify_jwt_in_request
        try:
            verify_jwt_in_request()
        except Exception:
            return redirect(url_for('welcome'))
        student = None
        try:
            from backend.models.student import Student
            student = Student.query.get(student_id)
        except Exception:
            pass
        if not student:
            app.logger.debug(f"Query failed! student_id={student_id}, type={type(student_id)}")
            flash("学生不存在")
            return redirect(url_for('interventions_page'))
        try:
            from backend.models.intervention import InterventionRecord
            interventions = InterventionRecord.query.filter_by(student_id=student_id)\
                          .order_by(InterventionRecord.operation_time.desc()).all()
        except Exception:
            interventions = []
        return render_template("student_detail.html", student=student, interventions=interventions)

    @app.route('/vision-records')
    def vision_record_page():
        from flask_jwt_extended import verify_jwt_in_request
        try:
            verify_jwt_in_request()
        except Exception:
            return redirect(url_for('welcome'))
        student_id = request.args.get('student_id')
        auto_expand = request.args.get('auto_expand')
        from_complete = request.args.get('from_complete')
        try:
            from backend.constants import SCHOOL_OPTIONS
        except Exception:
            SCHOOL_OPTIONS = None
        return render_template('vision_record.html', 
                         SCHOOL_OPTIONS=SCHOOL_OPTIONS,
                         current_student_id=student_id,
                         auto_expand=auto_expand,
                         from_complete=from_complete)

    @app.route('/student-complete-record')
    def student_complete_record_page():
        from flask_jwt_extended import verify_jwt_in_request
        try:
            verify_jwt_in_request()
        except Exception:
            return redirect(url_for('welcome'))
        try:
            from backend.constants import SCHOOL_OPTIONS
        except Exception:
            SCHOOL_OPTIONS = None
        return render_template('student_complete_record.html', SCHOOL_OPTIONS=SCHOOL_OPTIONS)

    @app.route('/test_intervention_config.html')
    def test_intervention_config():
        return send_from_directory('.', 'test_intervention_config.html')

    @app.route('/vision_record')
    def vision_record():
        student_id = request.args.get('student_id')
        auto_expand = request.args.get('auto_expand', 'false')
        from_complete = request.args.get('from_complete', 'false')
        name = request.args.get('name', '')
        education_id = request.args.get('education_id', '')
        try:
            from backend.constants import SCHOOL_OPTIONS
        except Exception:
            SCHOOL_OPTIONS = None
        return render_template('vision_record.html', 
                         student_id=student_id,
                         auto_expand=auto_expand,
                         from_complete=from_complete,
                         name=name,
                         education_id=education_id,
                         SCHOOL_OPTIONS=SCHOOL_OPTIONS)

    return app